﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IChessEngine;

namespace ChessEngine.Gaming
{
    public class ChsValidator : IChsValidator
    {
        public bool ValidateMovement(IChsPosition position, out IChsPosition nextPosition, IChsMovement movement)
        {
            //IF NOT posicion.ocupado_amigo(movement.destino) OR
            //NOT tablero.esta_dentro(movement.destino) OR
            //(movement.destino.x = pieza.x AND movement.destino.y = pieza.y)
            //  FALLO
            //ELSE
            //  IF NOT ValidarMovimientoSinJaque()
            //      FALLO
            //  RETURN NOT posicion.ejecutar(movement).rey_amigo_en_jaque;
            nextPosition = null;
            IChsBoard board = position.GetBoard();
            IChsSquare source = movement.Source;
            IChsSquare target = movement.Target;
            if (!position.HasAllyPiece(movement.Source) ||
                !position.GetBoard().IsInside(movement.Target) ||
                ((movement.Target.X == movement.Source.X) && (movement.Target.Y == movement.Source.Y)))
                return false;
            else
            {
                if (!ValidateMovementWithoutCheck(position, movement))
                    return false;
                nextPosition = position.Execute(movement);
                return !nextPosition.EnemyKingCheck;
            }
        }
        public bool ValidateMovementWithoutCheck(IChsPosition position, IChsMovement movement)
        {
            IChsPiece piece = position.GetPieceAt(movement.Source);
            IChsBoard board = position.GetBoard();
            IChsSquare source = movement.Source;
            IChsSquare target = movement.Target;
            int incX = 0;
            int incY = 0;
            int x = 0;
            int y = 0;
            //SWITCH pieza.tipo
            switch (piece.Type)
            {
                case EChsPieceType.PAWN:
                    //  CASE PEON
                    #region PEON
                    //      IF movement.PromotingType NOT NULL AND 
                    //      destino.y != final_de_tablero
                    //          FALLO
                    //      IF pieza.x==movement.destino.x AND
                    //      pieza.y+1==movement.destino.y
                    //          IF posicion.ocupado(movement.destino)
                    //              FALLO
                    //          ELSE
                    //              EXITO
                    //      IF pieza.x==movement.destino.x AND
                    //      pieza.y==segunda_fila AND
                    //      movement.destino.y==cuarta_fila
                    //          IF posicion.ocupado(movement.destino)
                    //              FALLO
                    //          ELSE
                    //              EXITO
                    //      IF pieza.x-1==movement.destino.x AND
                    //      pieza.y+1==movement.destino.y
                    //          IF posicion.ocupado_enemigo(movement.destino)
                    //              EXITO
                    //          ELSE
                    //              IF posicion.ocupado_enemigo(pieza.x-1, pieza.y) AND
                    //              posicion.pieza_en(pieza.x-1, pieza.y).comible_al_paso
                    //                  EXITO
                    //              ELSE
                    //                  FALLO
                    //      IF pieza.x+1==movement.destino.x AND
                    //      pieza.y+1==movement.destino.y
                    //          IF posicion.ocupado_enemigo(movement.destino)
                    //              EXITO
                    //          ELSE
                    //              IF posicion.ocupado_enemigo(pieza.x-1, pieza.y) AND
                    //              posicion.pieza_en(pieza.x-1, pieza.y).comible_al_paso
                    //                  EXITO
                    //              ELSE
                    //                  FALLO
                    if (position.ColorToMove == EChsPieceColor.WHITE)
                    {
                        //le toca mover al blanco
                        if ((movement.PromotingType != EChsPieceType.NONE) &&
                           ((target.Y != board.Height-1) ||
                            (movement.PromotingType == EChsPieceType.PAWN) ||
                            (movement.PromotingType == EChsPieceType.KING) ))
                            return false;
                        else if ((source.X == target.X) &&
                            (source.Y + 1 == target.Y))
                        {
                            if (position.HasPiece(target))
                                return false;
                            else
                                return true;
                        }
                        else if ((source.X == target.X) &&
                            (source.Y == 1) &&
                            (target.Y == 3) )
                        {
                            if (position.HasPiece(target))
                                return false;
                            else
                                return true;
                        }
                        else if ((source.X - 1 == target.X) &&
                            (source.Y + 1 == target.Y))
                        {
                            if (position.HasEnemyPiece(target))
                                return true;
                            else if (position.HasEnemyPiece(source.X - 1, source.Y) &&
                                     (position.GetPieceAt(source.X - 1, source.Y).OnPassant))
                                return true;
                            else
                                return false;
                        }
                        else if ((source.X + 1 == target.X) &&
                            (source.Y + 1 == target.Y))
                        {
                            if (position.HasEnemyPiece(target))
                                return true;
                            else if (position.HasEnemyPiece(source.X + 1, source.Y) &&
                                     (position.GetPieceAt(source.X + 1, source.Y).OnPassant))
                                return true;
                            else
                                return false;
                        }
                        else
                            return false;
                    }
                    else
                    {
                        //le toca mover al negro
                        if ((movement.PromotingType != EChsPieceType.NONE) &&
                            ((target.Y != 0) ||
                            (movement.PromotingType == EChsPieceType.PAWN) ||
                            (movement.PromotingType == EChsPieceType.KING) ))
                            return false;
                        else if ((source.X == target.X) &&
                            (source.Y - 1 == target.Y))
                        {
                            if (position.HasPiece(target))
                                return false;
                            else
                                return true;
                        }
                        else if ((source.X == target.X) &&
                            (source.Y == board.Height - 2) &&
                            (target.Y == board.Height - 4))
                        {
                            if (position.HasPiece(target))
                                return false;
                            else
                                return true;
                        }
                        else if ((source.X - 1 == target.X) &&
                            (source.Y - 1 == target.Y))
                        {
                            if (position.HasEnemyPiece(target))
                                return true;
                            else if (position.HasEnemyPiece(source.X - 1, source.Y) &&
                                     (position.GetPieceAt(source.X - 1, source.Y).OnPassant))
                                return true;
                            else
                                return false;
                        }
                        else if ((source.X + 1 == target.X) &&
                            (source.Y - 1 == target.Y))
                        {
                            if (position.HasEnemyPiece(target))
                                return true;
                            else if (position.HasEnemyPiece(source.X + 1, source.Y) &&
                                     (position.GetPieceAt(source.X + 1, source.Y).OnPassant))
                                return true;
                            else
                                return false;
                        }
                        else
                            return false;

                    }
                    #endregion
                case EChsPieceType.KNIGHT:
                    //  CASE CABALLO
                    #region CABALLO
                    //      difx = movement.destino.x - pieza.x
                    //      dify = movement.destino.y - pieza.y
                    //      IF (((difx==-1) OR (difx==1)) AND ((dify==-2) OR (dify==2))) OR
                    //      (((dify==-1) OR (dify==1)) AND ((difx==-2) OR (difx==2)))
                    //          IF posicion.ocupado_amigo(movement.destino)
                    //              FALLO
                    //          ELSE
                    //              EXITO
                    //      ELSE
                    //          FALLO
                    if ((((movement.DifX == -1) || (movement.DifX == 1)) && ((movement.DifY == -2) || (movement.DifY == 2))) ||
                    (((movement.DifY == -1) || (movement.DifY == 1)) && ((movement.DifX == -2) || (movement.DifX == 2))))
                    {
                       if (position.HasAllyPiece(target))
                            return false;
                        else
                            return true;
                    }
                    else
                    {
                        return false;
                    }
                    #endregion
                case EChsPieceType.BISHOP:
                    //  CASE ALFIL
                    #region ALFIL
                    //      difx = movement.destino.x - pieza.x
                    //      dify = movement.destino.y - pieza.y
                    //      IF difx==-dify OR dify==difx
                    //          IF posicion.ocupado_amigo(movement.destino)
                    //              FALLO
                    //          ELSE
                    //              FOR x IN movement.destino.x ... pieza.x
                    //              FOR y IN movement.destino.y ... pieza.y
                    //                  IF posicion.ocupado(x,y)
                    //                      FALLO
                    //              EXITO
                    //      ELSE
                    //          FALLO
                    if ((movement.DifX == -movement.DifY) || (movement.DifX == movement.DifY))
                    {
                        if (position.HasAllyPiece(target))
                            return false;
                        else
                        {
                            incX = (source.X > target.X) ? 1 : ((source.X == target.X) ? 0 : -1);
                            incY = (source.Y > target.Y) ? 1 : ((source.Y == target.Y) ? 0 : -1);
                            x = target.X+incX;
                            y = target.Y+incY;
                            for (;(y != source.Y) || (x != source.X);y += incY, x += incX)
                            {
                                if (position.HasPiece(x, y))
                                    return false;
                            }
                            return true;
                        }
                    }
                    else
                        return false;
                    #endregion
                case EChsPieceType.ROCK:
                    //  CASE TORRE
                    #region TORRE
                    //      difx = movement.destino.x - pieza.x
                    //      dify = movement.destino.y - pieza.y
                    //      IF difx==0 OR dify==0
                    //          IF posicion.ocupado_amigo(movement.destino)
                    //              FALLO
                    //          ELSE
                    //              FOR x IN movement.destino.x ... pieza.x
                    //              FOR y IN movement.destino.y ... pieza.y
                    //                  IF posicion.ocupado(x,y)
                    //                      FALLO
                    //              EXITO
                    //      ELSE
                    //          FALLO
                    if ((movement.DifX == 0) || (movement.DifY == 0))
                    {
                        if (position.HasAllyPiece(target))
                            return false;
                        else
                        {
                            incX = (source.X > target.X) ? 1 : ((source.X == target.X) ? 0 : -1);
                            incY = (source.Y > target.Y) ? 1 : ((source.Y == target.Y) ? 0 : -1);
                            x = target.X + incX;
                            y = target.Y + incY;
                            for (; (y != source.Y) || (x != source.X); y += incY, x += incX)
                            {
                                if (position.HasPiece(x, y))
                                    return false;
                            }
                            return true;
                        }
                    }
                    else
                        return false;
                    #endregion
                case EChsPieceType.QUEEN:
                    //  CASE DAMA
                    #region DAMA
                    //      difx = movement.destino.x - pieza.x
                    //      dify = movement.destino.y - pieza.y
                    //      IF difx==0 OR dify==0 OR difx==-dify OR dify==difx
                    //          IF posicion.ocupado_amigo(movement.destino)
                    //              FALLO
                    //          ELSE
                    //              FOR x IN movement.destino.x ... pieza.x
                    //              FOR y IN movement.destino.y ... pieza.y
                    //                  IF posicion.ocupado(x,y)
                    //                      FALLO
                    //              EXITO
                    //      ELSE
                    //          FALLO
                    if ((movement.DifX == 0) || (movement.DifY == 0) || 
                        (movement.DifX == -movement.DifY) || (movement.DifX == movement.DifY))
                    {
                        if (position.HasAllyPiece(target))
                            return false;
                        else
                        {
                            incX = (source.X > target.X) ? 1 : ((source.X == target.X) ? 0 : -1);
                            incY = (source.Y > target.Y) ? 1 : ((source.Y == target.Y) ? 0 : -1);
                            x = target.X + incX;
                            y = target.Y + incY;
                            for (; (y != source.Y) || (x != source.X); y += incY, x += incX)
                            {
                                if (position.HasPiece(x, y))
                                    return false;
                            }
                            return true;
                        }
                    }
                    else
                        return false;
                    #endregion
                case EChsPieceType.KING:
                    //  CASE REY
                    #region REY
                    //      difx = movement.destino.x - pieza.x
                    //      dify = movement.destino.y - pieza.y
                    //      IF difx<=1 AND dify<=1 AND difx>=-1 AND dify>=-1
                    //          IF posicion.ocupado_amigo(movement.destino)
                    //              FALLO
                    //          ELSE
                    //              EXITO
                    //      ELSE
                    //          FALLO
                    //      IF dos_menos AND posicion.enroque_largo
                    //          EXITO
                    //      IF dos_mas AND posicion.enroque_corto
                    //          EXITO
                    //      ELSE
                    //          FALLO
                    if ((movement.DifX >= -1) && (movement.DifX <= 1) &&
                        (movement.DifY >= -1) && (movement.DifY <= 1))
                    {
                        if (position.HasAllyPiece(target))
                            return false;
                        else
                            return true;
                    }
                    if (position.ColorToMove == EChsPieceColor.WHITE)
                    {
                        if ((source.Y == target.Y) &&
                            (source.Y == 0) &&
                            (source.X == 4) &&
                            (target.X == 2) &&
                            position.WhiteCastleQueenSide)
                        {
                            return true;
                        }
                        else if ((source.Y == target.Y) &&
                            (source.Y == 0) &&
                            (source.X == 4) &&
                            (target.X == 6) &&
                            position.WhiteCastleKingSide)
                        {
                            return true;
                        }
                        else
                            return false;
                    }
                    else
                    {
                        if ((source.Y == target.Y) &&
                            (source.Y == board.Height-1) &&
                            (source.X == 4) &&
                            (target.X == 2) &&
                            position.BlackCastleQueenSide)
                        {
                            return true;
                        }
                        else if ((source.Y == target.Y) &&
                            (source.Y == board.Height - 1) &&
                            (source.X == 4) &&
                            (target.X == 6) &&
                            position.BlackCastleKingSide)
                        {
                            return true;
                        }
                        else
                            return false;
                    }
                    #endregion
                default:
                    return false;
            }
        }
        public bool IsMovementUniqueByType(IChsGamePosition currentPosition, IChsGameMovement movement)
        {
            IChsPosition nextPosition = null;
            IChsPiece movedPiece = currentPosition.GetPieceAt(movement.Source);
            if (movedPiece == null)
                return true;
            IChsBoard board = currentPosition.GetBoard();
            for (int y = 0; y < board.Height; y++)
                for (int x = 0; x < board.Width; x++)
                {
                    IChsPiece piece = currentPosition.GetPieceAt(x,y);
                    if ((piece != null) &&
                        ((x != movement.Source.X) || (y != movement.Source.Y)) &&
                        (piece.Type == movedPiece.Type) &&
                        (ValidateMovement(currentPosition, out nextPosition,
                         ChsFactory.CreateMovement(ChsFactory.CreateSquare(x, y), movement.Target, EChsPieceType.QUEEN))))
                        return false;
                }
            return true;
        }
    }
}
